;; "[...] the only thing we need to know about these three operations
;; is that if we glue two objects together using cons we can retrieve
;; the objects using car and cdr.
;; That is, the operations satisfy the condition that, for any objects
;; x and y, if z is (cons x y) then (car z) is x and (cdr z) is y."
(define (cons x y)
  (define (dispatch m)
    (cond ((= m 0) x)
          ((= m 1) y)
          (else (error "Argument not 0 or 1: CONS" m))))
  ;; Return a dispatch, something that reacts in various ways,
  ;; depending on the input. In this case the dispatch's environment
  ;; contains x and y, the two parts of the pair we want to
  ;; build. Thus it can return any one of them.
  dispatch)

;; The procedure car expects to be given a pair and a pair is
;; represented by the dispatch we have defined as return value in
;; cons. It gives the dispatch a 0 as an argument, which dispatches to
;; the first part of the pair.
(define (car z)
  (z 0))

;; The procedure cdr expects to be given a pair and a pair is
;; represented by the dispatch we have defined as return value in
;; cons. It gives the dispatch a 1 as an argument, which dispatches to
;; the second part of the pair.
(define (cdr z)
  (z 1))


;; Lets try it out!
(use-modules (srfi srfi-64))

(test-begin "test-cons-car-cdr")
(test-group
 "test-cons-car-cdr"
 (test-equal
     (car (cons 5 4)) 5)
 (test-equal
     (cdr (cons 5 4)) 4))

(test-end "test-cons-car-cdr")

;; It worked. So to gain the ability of building complex data, nothing
;; more than procedures and application is required. Amazing!  This
;; also shows, that it is theoretically possible to represent any kind
;; of data as procedures and calls with arguments of primitive data.

;; "[...] if we access pairs using only cons, car, and cdr we cannot
;; distinguish this implementation from one that uses “real” data
;; structures."

;; " This example also demonstrates that the ability to manipulate
;; procedures as objects automatically provides the ability to
;; represent compound data.  "
